#include "my_structs.h"
#include <vector>
#include <limits>
#include <algorithm>
#include "gmp.h"
#include "gmpxx.h"

std::istream& operator>>(std::istream& in, point_t& p)
{
	in >> p.x >> p.y;
	return in;
}

void init(mpq_t ax, mpq_t ay, mpq_t bx, mpq_t by, mpq_t cx, mpq_t cy, mpq_t dx, mpq_t dy)
{
	mpq_init(ax);
	mpq_init(bx);
	mpq_init(cx);
	mpq_init(dx);
	mpq_init(ay);
	mpq_init(by);
	mpq_init(cy);
	mpq_init(dy);
}

void clear(mpq_t ax, mpq_t ay, mpq_t bx, mpq_t by, mpq_t cx, mpq_t cy, mpq_t dx, mpq_t dy)
{
	mpq_clear(ax);
	mpq_clear(bx);
	mpq_clear(cx);
	mpq_clear(dx);
	mpq_clear(ay);
	mpq_clear(by);
	mpq_clear(cy);
	mpq_clear(dy);
}

int gmp_left_turn(point_t a, point_t b, point_t c, point_t d)
{
	mpq_t ax, ay, bx, by, cx, cy, dx, dy;
	init(ax, ay, bx, by, cx, cy, dx, dy);

	mpq_set_d(ax, a.x);
	mpq_set_d(ay, a.y);
	mpq_set_d(bx, b.x);
	mpq_set_d(by, b.y);
	mpq_set_d(cx, c.x);
	mpq_set_d(cy, c.y);
	mpq_set_d(dx, d.x);
	mpq_set_d(dy, d.y);

	mpq_sub(bx, bx, ax);
	mpq_sub(by, by, ay);
	mpq_sub(cx, dx, cx);
	mpq_sub(cy, dy, cy);

	mpq_mul(ax, bx, cy);
	mpq_mul(ay, cx, by);

	mpq_sub(ax, ax, ay);

	int res = mpq_sgn(ax);
	clear(ax, ay, bx, by, cx, cy, dx, dy);

	return res;
}

int left_turn(point_t a, point_t b, point_t c, point_t d)
{
	double imprecision = 8 * std::numeric_limits<double>::epsilon()
			* (std::abs((b.x - a.x) * (d.y - c.y))
					+ std::abs((b.y - a.y) * (d.x - c.x)));
	double product = (b.x - a.x) * (d.y - c.y) - (b.y - a.y) * (d.x - c.x);

	if (product > imprecision)
		return 1;
	if (product < -imprecision)
		return -1;
	return gmp_left_turn(a, b, c, d);
}

int left_turn(point_t a, point_t b, point_t c)
{
	return left_turn(a, b, a, c);
}

int compare_dist_gmp(point_t a, point_t b, point_t c, point_t d)
{
	mpq_t ax, ay, bx, by, cx, cy, dx, dy;
	init(ax, ay, bx, by, cx, cy, dx, dy);

	mpq_set_d(ax, a.x);
	mpq_set_d(ay, a.y);
	mpq_set_d(bx, b.x);
	mpq_set_d(by, b.y);
	mpq_set_d(cx, c.x);
	mpq_set_d(cy, c.y);
	mpq_set_d(dx, d.x);
	mpq_set_d(dy, d.y);

	mpq_sub(ax,bx,ax);
	mpq_sub(ay,by,ay);
	mpq_sub(cx,dx,cx);
	mpq_sub(cy,dy,cy);

	mpq_mul(ax,ax,ax);
	mpq_mul(ay,ay,ay);
	mpq_mul(cx,cx,cx);
	mpq_mul(cy,cy,cy);

	mpq_add(ax,ax,ay);
	mpq_add(cx,cx,cy);

	mpq_sub(ax,ax,cx);

	int res = mpq_sgn(ax);
	clear(ax, ay, bx, by, cx, cy, dx, dy);

	return res;
}

int compare_dist(point_t a, point_t b, point_t c, point_t d)
{
	double x1 = (b.x - a.x) * (b.x - a.x);
	double x2 = (d.x - c.x) * (d.x - c.x);
	double y1 = (b.y - a.y) * (b.y - a.y);
	double y2 = (d.y - c.y) * (d.y - c.y);

	double imprecision = 8 * std::numeric_limits<double>::epsilon() * (x1 + y1 + x2 + y2);

	double res = (x1 + y1) - (x2 + y2);

	if (std::abs(res) > imprecision)
		if (res > 0)
			return 1;
		else
			return res < 0 ? -1 : 0;

	return compare_dist_gmp(a,b,c,d);
}

bool point_t::operator<(const point_t& p) const
{
	if (x != p.x)
	{
		return x < p.x;
	}
	return y < p.y;
}

std::ostream& operator<<(std::ostream& out, const std::vector<point_t>& v)
{
	out << v.size() << "\n";
	std::cout.precision(30);
	for (auto i = v.begin(); i < v.end(); i++)
	{
		out << (*i).x << " " << (*i).y << "\n";
	}

	return out;
}

bool point_t::operator==(const point_t& p) const
{
	return (x == p.x) && (y == p.y);
}
